library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity rx_top is

	port(
		clk  : in	std_logic;
		reset : in std_logic;
		-- mii signals
		rx_clk	 : in	std_logic; -- 25 MHz
		rxd : in std_logic_vector(3 downto 0);
		rx_dv : in std_logic;
		rx_er : in std_logic;
		--switch fabric interface
		rxd_fifo_rdreq  : in std_logic;
		rxd_fifo_empty : out std_logic;
		rxd_fifo_q : out std_logic_vector(7 downto 0);
		length_fifo_rdreq : in std_logic;
		length_fifo_empty : out std_logic;
		length_fifo_q : out std_logic_vector(11 downto 0);
		--test outputs for observation
		state_out : out std_logic_vector(1 downto 0)
	);

end entity;

architecture rtl of rx_top is
	type state_type is (
      idle, --waiting for rx_dv and sfd start 
      sfd_check, --check for second sfd nibble
      data_rx, --receive frame
      check --check crc, put length/err in fifo
		);
	signal state   : state_type;
	signal next_state : state_type;
	-- crc signals
	signal crc_init, crc_enable, crc_err : std_logic;
	-- info about current frame
	signal current_err, current_err_in, check_err : std_logic;
	signal current_length, current_length_in : unsigned (11 downto 0); --in nibbles
	-- data fifo signals
	signal rxd_fifo_wrreq, rxd_fifo_full : std_logic;
	-- length fifo signals
	signal length_fifo_wrreq, length_fifo_full : std_logic;
	-- constants
	constant sfd : std_logic_vector(7 downto 0) := "11010101"; 
	constant MAX_FRAME : unsigned(11 downto 0) := x"bdc"; --1518*2
	constant MIN_FRAME : unsigned(11 downto 0) := x"080"; --64*2
	
	component crc32 is
		port(
		  clk, init, enable: in std_logic;
		  data: in std_logic_vector(3 downto 0);
		  crc_err: out std_logic
--		  r_reg_out: out std_logic_vector(31 downto 0)
	    );
	end component;

	component rxd_fifo
		port
		(
		  aclr        : in std_logic ;
		  data		: in std_logic_vector (3 downto 0);
		  rdclk		: in std_logic ;
		  rdreq		: in std_logic ;
		  wrclk		: in std_logic ;
		  wrreq		: in std_logic ;
		  q		: out std_logic_vector (7 downto 0);
		  rdempty		: out std_logic ;
		  wrfull		: out std_logic 
		);
	end component;

	component Rlength_fifo
		port
		(
		  aclr		: in std_logic;
		  data		: in std_logic_vector (11 downto 0);
		  rdclk		: in std_logic ;
		  rdreq		: in std_logic ;
		  wrclk		: in std_logic ;
		  wrreq		: in std_logic ;
		  q		: out std_logic_vector (11 downto 0);
		  rdempty		: out std_logic ;
		  wrfull		: out std_logic 
		);
	end component;


begin
	process (rx_clk, reset)
	begin
    if (reset = '1') then
      state <= idle;
      current_length <= (others => '0');
      current_err <= '0';
    elsif (rx_clk'event and rx_clk = '1') then
      state <= next_state;
	  current_length <= current_length_in;
	  current_err <= current_err_in;
    end if;
    end process;
  
  next_state <=
    idle when
      (state = idle and not (rxd = sfd(3 downto 0) and rx_dv='1' and rxd_fifo_full='0')) or
      (state = sfd_check and not ((rxd = sfd(7 downto 4) or rxd = sfd(3 downto 0)) and rx_dv='1')) or
      (state = check) else
    sfd_check when 
      ((state = idle or state = sfd_check) and rxd = sfd(3 downto 0) and rx_dv='1' and rxd_fifo_full='0') else
    data_rx when 
      (state = sfd_check and rxd = sfd(7 downto 4) and rx_dv='1') or
      (state = data_rx and rx_dv='1') else
    check when 
      (state = data_rx and rx_dv='0') else
    idle;
      
  crc_init <= '1' when (state = idle) else '0';
    
  crc_enable <= rxd_fifo_wrreq;
  rxd_fifo_wrreq <= '1' when ((rx_dv='1' or current_length(0)='1') and rxd_fifo_full='0' and state = data_rx and current_length < MAX_FRAME) else '0';
  
  current_err_in <= 
    '1' when state=data_rx and (current_err='1' or rx_er='1' or (current_length=MAX_FRAME and rx_dv='1') or (current_length(0)='1' and rx_dv='0')) else '0';
    
  check_err <= '1' when current_err='1' or crc_err='1' or (current_length < MIN_FRAME) else '0'; --receive, crc, odd nibbles, runt errors
  
  current_length_in <=
    current_length + x"001" when rxd_fifo_wrreq='1' else
    current_length when state = data_rx else
    x"000";
    
  length_fifo_wrreq <= '1' when (state=check and length_fifo_full='0') else '0';
    
      
	crc32_inst : crc32 port map (
		clk=>rx_clk, 
		init=>crc_init, 
		enable=>crc_enable, 
		data=>rxd,
		crc_err=>crc_err
	);
	
	rxd_fifo_inst : rxd_fifo port map (
		aclr     => reset,
		data	 => rxd,
		rdclk	 => clk,
		rdreq	 => rxd_fifo_rdreq,
		wrclk	 => rx_clk,
		wrreq	 => rxd_fifo_wrreq,
		q	 	 => rxd_fifo_q,
		rdempty	 => rxd_fifo_empty,
		wrfull	 => rxd_fifo_full
	);
	
	length_fifo_inst : Rlength_fifo port map (
		aclr	 => reset,
		data	 => (not check_err & std_logic_vector(current_length(11 downto 1))),
		rdclk	 => clk,
		rdreq	 => length_fifo_rdreq,
		wrclk	 => rx_clk,
		wrreq	 => length_fifo_wrreq,
		q	 => length_fifo_q,
		rdempty	 => length_fifo_empty,
		wrfull	 => length_fifo_full
	);
	
	state_out <= 
		"00" when (state=idle) else
		"01" when (state=sfd_check) else
		"10" when (state=data_rx) else
		"11";
	
end rtl;
